/*
 * Copyright (C) 2009 Christoph Caks
 *
 * This file is part of GLWAK.
 *
 * GLWAK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * GLWAK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with GLWAK.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * WindowsContext.cpp
 *
 *  Created on: 31.07.2009
 *      Author: Christoph Caks
 */

#include "windows/WindowsContext.h"

namespace glwak {

namespace priv {

WindowsContext::WindowsContext() {
	// use a dummy window

	if ( this->window == NULL ) {
		WindowConfiguration wc( "Context Dummy Window", 30, 30, false );
		this->window = new WindowsWindow();
		this->window->setConfiguration( wc );
		this->window->initialize();
	}

}

WindowsContext::WindowsContext( Window *_window ) : Context( _window ) {
	this->window = dynamic_cast<WindowsWindow*>( _window );
}

WindowsContext::~WindowsContext() {
	// TODO Auto-generated destructor stub
	// TODO something like:  if ( !this->isDisposed() ) this->dispose(); ( should come somehow from disposeable )
}

void *WindowsContext::getExtensionAddress( const char *_extension ) {
	// FIXME think about opengl extensions
//	void (*tmp)() = glXGetProcAddress( (const GLubyte*) _extension );
//	return &tmp;
}


void WindowsContext::initialize() {
	LOG_DEBUG4 << "[WindowsContext] initialize() - begin";
	deviceContext = GetDC( window->getWindow() );
	if ( ! deviceContext ) {
	   	const std::string error = glwak::util::WindowsHelper::getLastError();
		LOG_ERROR << "[WindowsContext] initialize() - could not get device context: " << error;
		throw error.c_str();
	}

	PIXELFORMATDESCRIPTOR pfd = {
	    /*nSize*/ 		sizeof(PIXELFORMATDESCRIPTOR),  // size of this pfd
	    /*nVersion*/ 	1,                     // version number
	    /*dwFlags*/ 	PFD_DRAW_TO_WINDOW |   // support window
						PFD_SUPPORT_OPENGL |   // support OpenGL
						PFD_DOUBLEBUFFER,      // double buffered
		/*iPixelTxpe*/	PFD_TYPE_RGBA,         // RGBA type
	    /*cColorBits*/	24,						// 24-bit color depth
	    0, 0, 0, 0, 0, 0,		// color bits ignored
	    /*cAlphaBits*/	0,		// no alpha buffer
	    0,						// shift bit ignored
	    /*cAccumBits*/	0,		// no accumulation buffer
	    0, 0, 0, 0,				// accum bits ignored
	    /*cDepthBits*/	configuration.getDepthBits(),	// 32-bit z-buffer
	    /*cStencilBits*/configuration.getStencilBits(), // stencil buffer bits
	    /*cAuxBuffers*/	0,		// no auxiliary buffer
	    /*iLayerType*/	PFD_MAIN_PLANE,        // main layer
	    /*bReserved*/	0,                     // reserved
	    0, 0, 0                // layer masks ignored
	};
	int  iPixelFormat;

	// get the best available match of pixel format for the device context
	iPixelFormat = ChoosePixelFormat( deviceContext, &pfd );
	if ( ! iPixelFormat ) {
	   	const std::string error = glwak::util::WindowsHelper::getLastError();
		LOG_ERROR << "[WindowsContext] initialize() - could not choose pixelformat: " << error;
		throw error.c_str();
	}
	// make that the pixel format of the device context
	BOOL retVal = SetPixelFormat( deviceContext, iPixelFormat, &pfd );
	if ( ! retVal ) {
	   	const std::string error = glwak::util::WindowsHelper::getLastError();
		LOG_ERROR << "[WindowsContext] initialize() - could not set pixelformat: " << error;
		throw error.c_str();
	}

//	LOG_DEBUG << wglGetExtenstionString( deviceContext );

	// create the dummy context for retrieving the function pointer to the new context creation function
	HGLRC glDummyContext = wglCreateContext( deviceContext );
	if ( ! glDummyContext ) {
	   	const std::string error = glwak::util::WindowsHelper::getLastError();
		LOG_ERROR << "[WindowsContext] initialize() - could not create dummy context: " << error;
		throw error.c_str();
	}

	wglMakeCurrent( deviceContext, glDummyContext );

	if ( GLEW_OK != glewInit() ) {
		LOG_ERROR << "GLEW not initialized!";
	}

	HGLRC sharedContext = NULL;
	PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = reinterpret_cast<PFNWGLCREATECONTEXTATTRIBSARBPROC>( wglGetProcAddress( "wglCreateContextAttribsARB" ) );
    if ( wglCreateContextAttribsARB ) {
        int attributes[] = {
            WGL_CONTEXT_MAJOR_VERSION_ARB, configuration.getMajorVersion(),
            WGL_CONTEXT_MINOR_VERSION_ARB, configuration.getMinorVersion(),
            0, 0
        };
        glRenderingContext = wglCreateContextAttribsARB( deviceContext, sharedContext, attributes );
    }
    else {
    	const std::string error = glwak::util::WindowsHelper::getLastError();
    	LOG_WARNING << "[WindowsContext] initialize() - could not create context with new method: " << error;
		LOG_WARNING << "[WindowsContext] falling back to old context creation";
		glRenderingContext = wglCreateContext( deviceContext );
    }

	if ( ! glRenderingContext ) {
	   	const std::string error = glwak::util::WindowsHelper::getLastError();
		LOG_ERROR << "[WindowsContext] initialize() - could not create context: " << error;
		throw error.c_str();
	}

	LOG_DEBUG4 << "[WindowsContext] initialize() - end";
}

void WindowsContext::makeCurrent() {
	LOG_DEBUG4 << "[WindowsContext] makeCurrent() - begin";
	wglMakeCurrent( deviceContext, glRenderingContext );
	LOG_DEBUG4 << "[WindowsContext] makeCurrent() - end";
}

void WindowsContext::swapBuffers() {
	LOG_DEBUG4 << "[WindowsContext] swapBuffers() - begin";
	SwapBuffers( deviceContext );
	LOG_DEBUG4 << "[WindowsContext] swapBuffers() - end";
}

void WindowsContext::dispose() {
	LOG_DEBUG4 << "[WindowsContext] dispose() - begin";
	if ( ! wglDeleteContext( glRenderingContext ) ) {
	   	const std::string error = glwak::util::WindowsHelper::getLastError();
		LOG_ERROR << "[WindowsContext] dispose() - could not dispose context: " << error;
		throw error.c_str();
	}
	LOG_DEBUG4 << "[WindowsContext] dispose() - end";
}

} // namespace priv

} // namespace glwak
